/*   Copyright © 2009 Mark Gil Torres
 *
 *   This file is part of mtoolkit.
 *
 *   mtoolkit is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   mtoolkit is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with mtoolkit.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef ALGEBRAICEXPRESSION_HPP
#define ALGEBRAICEXPRESSION_HPP

#include <string>
#include <vector>
#include "q.hpp"
#include "variable.hpp"
#include "term.hpp"

namespace mt {

  template <typename T> class AlgebraicExpression;
  template <typename T> AlgebraicExpression<T> const operator+(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
  template <typename T> AlgebraicExpression<T> const operator-(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
  template <typename T> AlgebraicExpression<T> const operator*(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
  template <typename T> AlgebraicExpression<T> const operator/(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);

  template <typename T= Q<int> >
  class AlgebraicExpression {
    public:
      AlgebraicExpression(void);
      AlgebraicExpression(Term<T> const& inValue);
      AlgebraicExpression(AlgebraicExpression<T> const& inValue);

      AlgebraicExpression<T>& operator=(std::vector<Term<T> > const& inValue);
      AlgebraicExpression<T>& operator=(AlgebraicExpression<T> const& inValue);

      std::vector<Term<T> >& getAE(void);

      void setAE(std::vector<Term<T> > const& inValue);

      friend AlgebraicExpression<T> const operator+ <>(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
      friend AlgebraicExpression<T> const operator- <>(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
      friend AlgebraicExpression<T> const operator* <>(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);
      friend AlgebraicExpression<T> const operator/ <>(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2);

      void combineLikeTerms(void);
    private:
      std::vector<Term<T> > ae;
  };

//constructor
  template <typename T>
  AlgebraicExpression<T>::AlgebraicExpression(void) {}

  template <typename T>
  AlgebraicExpression<T>::AlgebraicExpression(Term<T> const& inValue) {
    ae.push_back(inValue);
  }

  template <typename T>
  AlgebraicExpression<T>::AlgebraicExpression(AlgebraicExpression<T> const& inValue) : ae(inValue.ae) {}

//assignment operator
  template <typename T>
  AlgebraicExpression<T>& AlgebraicExpression<T>::operator=(std::vector<Term<T> > const& inValue) {
    ae = inValue;
  }

  template <typename T>
  AlgebraicExpression<T>& AlgebraicExpression<T>::operator=(AlgebraicExpression<T> const& inValue) {
    ae = inValue.ae;
  }


//getter
  template <typename T>
  std::vector<Term<T> >& AlgebraicExpression<T>::getAE(void) {
    return ae;
  }

//setter
  template <typename T>
  void AlgebraicExpression<T>::setAE(std::vector<Term<T> > const& inValue) {
    ae = inValue;
  }

  template <typename T>
  AlgebraicExpression<T> const operator+(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2) {
    AlgebraicExpression<T> temp;
    std::vector<Term<T> > iv1Temp = inValue1.ae;
    std::vector<Term<T> > iv2Temp = inValue2.ae;
    std::vector<Term<T> > aeTemp(iv1Temp);
    int sz_iv2 = iv2Temp.size();
    for (int i = 0;i < sz_iv2; i++) aeTemp.push_back(iv2Temp[i]);
    temp.ae = aeTemp;
    temp.combineLikeTerms();
    return temp;
  }

  template <typename T>
  AlgebraicExpression<T> const operator-(AlgebraicExpression<T> const& inValue1, AlgebraicExpression<T> const& inValue2) {
    AlgebraicExpression<T> temp;
    std::vector<Term<T> > iv1Temp = inValue1.ae;
    std::vector<Term<T> > iv2Temp = inValue2.ae;
    std::vector<Term<T> > aeTemp(iv1Temp);
    int sz_iv2 = iv2Temp.size();
    for (int i = 0;i < sz_iv2; i++) aeTemp.push_back(negative(iv2Temp[i]));
    temp.ae = aeTemp;
    temp.combineLikeTerms();
    return temp;
  }

  template <typename T>
  void AlgebraicExpression<T>::combineLikeTerms(void) {
    std::vector<Term<T> > temp;
    std::vector<Term<T> > aeTemp;
    aeTemp = ae;
    Term<T> termTemp(0);
    const Term<T> zero(0);
    for (int i = 0; i < aeTemp.size(); i++) {
      termTemp = aeTemp[i];
      for (int j = i+1; j < aeTemp.size(); j++) {
        if (areLikeTerms(aeTemp[i],aeTemp[j]) == true) {
          termTemp = termTemp + aeTemp[j];
          aeTemp[j] = zero;
//          aeTemp.erase(j);
        }
      }
      temp.push_back(termTemp);
    }
//    ae.resize(temp.size());
    ae = temp;
  }


}

#endif

